--[[
    Tabard by LordFarlander
    Version 2.5.49
    $Revision: 215 $

    Based on Stable.
]]--

--[[
Copyright (c) 2008, LordFarlander
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
]]--

local L = LibStub( "AceLocale-3.0" ):GetLocale( "Tabard" );

Tabard = LibStub( "AceAddon-3.0" ):NewAddon( "Tabard", "AceEvent-3.0", "AceConsole-3.0" );
Tabard.version = { major = 2, minor = 5, revision = tonumber( "49" ) };

Tabard.tabards = { normal = { bag = {}, slot = {}, num = 0 }, };

Tabard.seenTabards = {};
Tabard.ui = { Favorites = {}, currentFavorite = 0, };

Tabard.OutfitManagers = {
    ["Outfitter"] = {
        CheckAndCreate = function( Outfitter, itemName, itemLink )
                if( not Outfitter:FindOutfitByName( itemName ) ) then
                    local newOutfit = Outfitter:NewEmptyOutfit( itemName );
                    
                    Outfitter:AddOutfit( newOutfit );
                    Outfitter:AddOutfitItem( newOutfit, "TabardSlot", Outfitter:GetItemInfoFromLink( itemLink ) );
                end--if
            end,
        Swap = function( Outfitter, CurrentTabard, NewTabard )
                local outfit = Outfitter:FindOutfitByName( CurrentTabard );
        
                if( outfit ) then
                    Outfitter:RemoveOutfit( outfit );
                end--if
                outfit = Outfitter:FindOutfitByName( NewTabard );
                if( outfit ) then
                    Outfitter:WearOutfit( outfit );
                end--if        
            end,
    },
    ["ItemRack"] = {
        CheckAndCreate = function( ItemRack, itemName, itemLink )
                local ItemRackUserSets = _G["ItemRackUser"].Sets;

                if( not ItemRackUserSets[itemName] ) then
                    -- we have to build ourselves :(
                	ItemRackUserSets[itemName] = { icon = select( 10, GetItemInfo( itemLink ) ), old = {}, equip = { [GetInventorySlotInfo( "TabardSlot" )] = ("%i:0:0:0:0:0:0"):format( tonumber( itemLink:match( "(%d+)" ) ) ), }, }
                end--if
            end,
        Swap = function( ItemRack, CurrentTabard, NewTabard )
                local ItemRackUserSets = _G["ItemRackUser"].Sets;
        
                if( ItemRackUserSets and ItemRackUserSets[CurrentTabard] ) then
                    ItemRack.UnequipSet( CurrentTabard );
                end--if
                if( ItemRackUserSets and ItemRackUserSets[NewTabard] ) then
                    ItemRack.EquipSet( NewTabard );
                end--if       
            end,
    },
    ["ClosetGnome"] = {
        CheckAndCreate = function( ClosetGnome, itemName, itemLink )
                if( not ClosetGnome:HasSet( itemName ) ) then
                    -- we have to build ourselves :(
                    if( not ClosetGnome.db.char.set ) then
                        ClosetGnome.db.char.set = {};
                    end--if
                    ClosetGnome.db.char.set[itemName] = { [GetInventorySlotInfo( "TabardSlot" )] = itemLink, };
                	ClosetGnome:TriggerEvent( "ClosetGnome_AddSet", itemName );
                	ClosetGnome:Print( L["Added set: %s."]:format( itemName ) );
                	ClosetGnome:UpdateSetOptions( itemName, itemLink );
                end--if
            end,
        Swap = function( ClosetGnome, CurrentTabard, NewTabard )
                if( ClosetGnome:HasSet( NewTabard ) ) then
                    ClosetGnome:WearSet( NewTabard );
                end--if
            end,
    },
};

function Tabard:OnInitialize()
    local AceConfigReg = LibStub( "AceConfigRegistry-3.0" );
    local AceConfigDialog = LibStub( "AceConfigDialog-3.0" );
    local defaults = {
        profile = {
            autoCreateSets = false;
            -- List of favorite tabards, these will be picked more often
            -- stored as [itemID]=percentage
            Favorites = {},
        },
    };
    local favoritesOptions = {
        type = "group",
        name = L["Favorites"],
        desc = L["Tabards that should be picked more often if you have them."],
        handler = self,
        args = {
            favorite = {
                type = "select",
                order = 1,
                name = L["Favorites"],
                desc = L["Tabards that should be picked more often if you have them."],
                width = "full",
                cmdHidden = true,
                values = function( info )
                             info.handler.ui.Favorites = {};

                             local Favorites = info.handler.ui.Favorites;
                             local firstItem = 0;
                             local used = {};

                             for itemLink, _ in pairs( info.handler.seenTabards ) do
                                 local id = tonumber( itemLink:match( "^|c%x+|H(.+)|h%[.+%]" ):match( "(%d+)" ) );

                                 used[id] = true;
                                 table.insert( Favorites, id );
                             end--for
                             for tabard, howOften in pairs( info.handler.db.profile.Favorites ) do
                                 if( not used[tabard] ) then
                                     table.insert( Favorites, tabard );
                                 end--if
                             end--if
                             table.sort( Favorites, function( a, b ) return GetItemInfo( a ) < GetItemInfo( b ) end );
                             Favorites = {};
                             for index, tabard in pairs( info.handler.ui.Favorites ) do
                                 local sName, sTexture;
                                 local sHowOften = L["default"];
                                 local howOften = info.handler.db.profile.Favorites[tabard] or 1.0;

                                 sName, _, _, _, _, _, _, _, _, sTexture = GetItemInfo( tabard );
                                 if( howOften == 0.0 ) then
                                     sHowOften = L["never"];
                                 elseif( howOften ~= 1.0 ) then
                                     sHowOften = L["%.1f times as often"]:format( howOften );
                                 end--if                                 
                                 if( sTexture ) then
                                     Favorites[index] = ("|T%s:0|t %s |cFFFF0000[%s]|r"):format( sTexture, sName, sHowOften );
                                 end--if
                                 if( firstItem == 0 ) then
                                     firstItem = index;
                                 end--if
                             end--for
                             if( ( not Favorites[info.handler.ui.currentFavorite] ) or ( info.handler.ui.currentFavorite == 0 ) ) then
                                 info.handler.ui.currentFavorite = firstItem;
                             end--if
                             return Favorites;
                         end,
                get = function( info )
                          return info.handler.ui.currentFavorite;
                      end,
                set = function( info, val )
                          info.handler.ui.currentFavorite = val;
                      end,
            },
            frequency = {
                type = "range",
                order = 2,
                name = L["Frequency"],
                cmdHidden = true,
                min = 0.0,
                max = 11.0,
                bigStep = 0.1,
                isPercent = true,
                desc = L["Set how often the tabard gets picked.  2 is twice as often and 0 is never."];
                get = function( info )
                          return info.handler.db.profile.Favorites[info.handler.ui.Favorites[info.handler.ui.currentFavorite]] or 1.0;
                      end,
                set = function( info, val )
                          if( val == 1.0 ) then
                              info.handler.db.profile.Favorites[info.handler.ui.Favorites[info.handler.ui.currentFavorite]] = nil;
                          else
                              info.handler.db.profile.Favorites[info.handler.ui.Favorites[info.handler.ui.currentFavorite]] = val;
                          end--if
                      end,
            },
            [L["list"]] = {
                type = "execute",
                order = 4,
                guiHidden = true,
                name = L["List all favorite tabards"],
                desc = L["Show a list with all of your favorite tabards."],
                func = function( info )
                           info.handler:Print( L["List of your favorite tabards:"] );
                           for tabard, howOften in pairs( info.handler.db.profile.Favorites ) do
                               if( howOften ~= 1.0 ) then
                                   local link = LibLordFarlander.GetItemLink( tabard );
                                   local sTexture = select( 10, GetItemInfo( link ) );

                                   if( howOften == 0.0 ) then
                                       howOften = L["never"];
                                   else
                                       howOften = L["%.1f times as often"]:format( howOften );
                                   end--if

                                   if( sTexture ) then
                                       info.handler:Print( ("|T%s:0|t %s |cFFFF0000[%s]|r"):format( sTexture, link or tostring( tabard ), howOften ) );
                                   else
                                       info.handler:Print( ("%s |cFFFF0000[%s]|r"):format( link or tostring( tabard ), howOften ) );
                                   end--if
                               end--if
                           end--for
                       end,
            },
        },
    };
    local options = {
        type = "group",
        handler = self,
        args = {
            [L["autoCreateSets"]] = {
                type = "toggle",
                order = 1,
                name = L["Auto create sets"],
                desc = function( info )
                            local returnString = L["Toggles if sets/outfits should be created automatically for tabards if you have a suppored outfit addon installed (ItemRack, Outfitter, or ClosetGnome)."];

                            if( info.uiType == "cmd" ) then
                                returnString = ("%s |cFFFF0000[%s]|r"):format( returnString, info.handler.db.profile.FavorFlying and L["yes"] or L["no"] );
                            end--if
                           return returnString;
                       end,
                get = function( info )
                          return info.handler.db.profile.autoCreateSets;
                      end,
                set = function( info, val )
                          info.handler.db.profile.autoCreateSets = val;
                      end,
            },
        },
        name = L["Tabard"],
        desc = L["Addon options."],
        icon = "Interface\\Icons\\INV_Shirt_GuildTabard_01",
    };

    self.db = LibStub( "AceDB-3.0" ):New( "TabardDB", defaults );
    self.db.RegisterCallback( self, "OnProfileChanged" );
    self.db.RegisterCallback( self, "OnProfileReset" );
    
    local profileOptions = LibStub( "AceDBOptions-3.0" ):GetOptionsTable( self.db );
    local slashOptions = {
        type = "group",
        handler = self,
        name = options.name,
        desc = options.desc,
        icon = options.icon,
        args = {
            [L["options"]] = options,
            [L["favorites"]] = favoritesOptions,
            [L["profile"]] = profileOptions,
            [L["gui"]] = {
                type = "execute",
                order = -1,
                name = L["Open GUI configurator"],
                desc = L["Open GUI configurator"],
                guiHidden = true,
                func = function( info )
                           InterfaceOptionsFrame_OpenToCategory( L[info.handler.name] );
                       end,
            },
            debug = {
                type = "toggle",
                hidden = true,
                name = "Debug",
                desc = "Debug mode toggle",
                desc = function( info )
                            local returnString = "Debug mode toggle";

                            if( info.uiType == "cmd" ) then
                                returnString = ("%s |cFFFF0000[%s]|r"):format( returnString, info.handler.DEBUG and L["yes"] or L["no"] );
                            end--if
                           return returnString;
                       end,
                get = function( info )
                          return info.handler.DEBUG;
                      end,
                set = function( info, val )
                          info.handler.DEBUG = val;
                          info.handler:Print( ("debug mode |cFFFF0000[%s]|r"):format( info.handler.DEBUG and L["yes"] or L["no"] ) );
                      end,
            },
        },
    };
    
    LibStub( "AceConfig-3.0" ):RegisterOptionsTable( L["Tabard"], slashOptions, { L["tabard"], L["tbd"] } );
    AceConfigReg:RegisterOptionsTable( "Tabard Main Options", options );
    AceConfigReg:RegisterOptionsTable( "Tabard Favorites", favoritesOptions );
    AceConfigReg:RegisterOptionsTable( "Tabard Profile", profileOptions );
    AceConfigDialog:AddToBlizOptions( "Tabard Main Options", L["Tabard"] );
    AceConfigDialog:AddToBlizOptions( "Tabard Favorites", L["Favorites"], L["Tabard"] );
    AceConfigDialog:AddToBlizOptions( "Tabard Profile", L["Profile"], L["Tabard"] );
end--Tabard:OnInitialize()

function Tabard:OnProfileReset()
    self:OnProfileChanged();
end--Tabard:OnProfileReset()

function Tabard:OnProfileChanged()
    self.db.profile.LastWoWVersion = tonumber( select( 4, GetBuildInfo() ) );
    self.db.profile.LastVersion = self.version;
end--Tabard:OnProfileChanged()

function Tabard:OnEnable()
    self:RegisterEvent( "BANKFRAME_OPENED" );
    self:RegisterEvent( "BANKFRAME_CLOSED",
        function()
            if( ( not Tabard.bankopen ) and ( not Tabard.swapped ) ) then
                Tabard:BANKFRAME_OPENED();
            end--if
            Tabard.bankopen = false;
        end );
    self:OnProfileChanged();
end--Tabard:OnEnable()

function Tabard:CheckAndCreateSet( itemName, itemLink )
    for name, funcs in pairs( self.OutfitManagers ) do
        local object = _G[name];
        
        if( object and funcs.CheckAndCreate ) then
            funcs.CheckAndCreate( object, itemName, itemLink );
        end--if
    end--for
end--Tabard:CheckAndCreateSet()

function Tabard:BANKFRAME_OPENED()
    local tabardSet = self.tabards;
    local autoCreateSets = self.db.profile.autoCreateSets;
    local seenTabards = self.seenTabards;
    local itemLink = GetInventoryItemLink( "player", GetInventorySlotInfo( "TabardSlot" ) );

    tabardSet.num = 0;
    tabardSet.bag = {};
    tabardSet.slot = {};
    self.bankopen = true;
    self.swapped = false;
    if( itemLink ) then
        tabardSet.num = 1;
        tabardSet.bag[1] = -255;
        tabardSet.slot[1] = GetInventorySlotInfo( "TabardSlot" );
        if( autoCreateSets ) then
            self:CheckAndCreateSet( select( 1, GetItemInfo( itemLink ) ), itemLink );
        end--if
        seenTabards[itemLink] = true;
        for bag = BANK_CONTAINER, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
            for slot = 1, GetContainerNumSlots( bag ) do
                itemLink = GetContainerItemLink( bag, slot );
                if( itemLink ) then
                    local itemName, _, _, _, _, _, _, _, itemEquipLoc, _ = GetItemInfo( itemLink );

                    if( itemEquipLoc == "INVTYPE_TABARD" ) then
                        local n = tabardSet.num + 1;

                        tabardSet.num = n;
                        tabardSet.bag[n] = bag;
                        tabardSet.slot[n] = slot;
                        if( autoCreateSets ) then
                            self:CheckAndCreateSet( itemName, itemLink );
                        end--if
                        seenTabards[itemLink] = true;
                    end--if
                 end--if
            end--for
        end--for
        if( tabardSet.num > 0 ) then
            self.swapped = true;
            self:DoSwaps( Tabard.tabards );
        end--if
    end--if
end--Tabard:BANKFRAME_OPENED()

------------------------------
--     Helper Functions     --
------------------------------
function Tabard.GetRandomFromListWithWeighing( set, favorites, default )
    if( not default ) then
        default = 1.0;
    end--if

    local total = 0.0;
    local useset = {};
    local randomValue;
    local amountOfItems = 0;
    local index = 0;

    for i = 1, set.num do
        local itemID = tonumber( string.match( ( set.bag[i] ~= -255 ) and GetContainerItemLink( set.bag[i], set.slot[i] ) or GetInventoryItemLink( "player", set.slot[i] ), "(%d+)" ) );
        local howOften = favorites[itemID] or default;

        if( howOften > 0.0 ) then
            tinsert( useset, i );
            total = total + howOften;
            amountOfItems = amountOfItems + 1;
        end--if
    end--for
    randomValue = math.random() * total;
    repeat
        index = index + 1;
        itemID = useset[index];
        if( itemID ) then
            randomValue = randomValue - ( favorites[tonumber( string.match( ( set.bag[itemID] ~= -255 ) and GetContainerItemLink( set.bag[itemID], set.slot[itemID] ) or GetInventoryItemLink( "player", set.slot[itemID] ), "(%d+)" ) )] or default );
        end--if
    until ( randomValue <= 0 ) or ( index == amountOfItems );
    return index;
end--Tabard:GetRandomFromListWithWeighing( set, favorites, default )

function Tabard:DoSwaps( tabardSet )
    local r = self.GetRandomFromListWithWeighing( tabardSet, self.db.profile.Favorites );
    local Outfitter = _G["Outfitter"];
    local ItemRack = _G["ItemRack"];
    local ClosetGnome = _G["ClosetGnome"];
    local CurrentTabard = GetItemInfo( GetInventoryItemLink( "player", GetInventorySlotInfo( "TabardSlot" ) ) );
    local NewTabard = GetItemInfo( GetContainerItemLink( tabardSet.bag[r], tabardSet.slot[r] ) );

    if( tabardSet.bag[r] ~= -255 ) then
        PickupContainerItem( tabardSet.bag[r], tabardSet.slot[r] );
        AutoEquipCursorItem();
        for name, funcs in pairs( self.OutfitManagers ) do
            local object = _G[name];

            if( object and funcs.Swap ) then
                funcs.Swap( object, CurrentTabard, NewTabard );
            end--if
        end--for
    end--if
end--Tabard:DoSwaps( type )
